# -*- coding: UTF-8 -*-


class classproperty:
    def __init__(self, method, *args, **kwargs):
        self.method = method

    def __get__(self, instance, owner):
        return self.method(owner)


class MetaModel(object):
    _model = None
    _env = None

    @classproperty
    def _name(cls):
        return cls._model

    @classproperty
    def res_model(cls):
        return cls._model

    @classproperty
    def env(cls):
        return cls._env

    @classmethod
    def execute_kw(cls, method, args, kwargs):
        kwargs2 = kwargs.copy()
        context = kwargs.get('context')

        if not context:
            kwargs2['context'] = cls.env.context

        return cls.env.web.dataset.call_kw(
            cls._name, method, args, kwargs2)

    @classmethod
    def execute(cls, method, *args):
        return cls.execute_kw(method, args, {})


class BaseModel(MetaModel):
    @classmethod
    def fields_get(cls, allfields=None, attributes=None):
        return cls.execute('fields_get', allfields, attributes)

    @classmethod
    def search(cls, domain, *args, **kwargs):
        return cls.execute_kw('search', [domain] + list(args), kwargs)

    @classmethod
    def web_search_read(cls, *args, **kwargs):
        return cls.execute_kw('web_search_read', args, kwargs)

    @classmethod
    def _format_dict(cls, one):
        def fmt(fld, val):
            meta = cls._fields.get(fld, {})
            if meta.get('type') == 'boolean':
                return val
            elif meta.get('type') == 'integer':
                return val or 0
            elif meta.get('type') in ['float', 'monetary']:
                return val or 0.0
            else:
                return val or None

        res = {}
        for k, v in one.items():
            res[k] = fmt(k, v)

        return res

    @classmethod
    def _format_result(cls, result):
        return [cls._format_dict(one) for one in result]

    @classmethod
    def search_read(cls, *args, **kwargs):
        res = cls.execute_kw('search_read', args, kwargs)
        return cls._format_result(res)

    @classmethod
    def read(cls, ids, *args, **kwargs):
        args2 = [ids] + list(args)
        res = cls.execute_kw('read', args2, kwargs)
        return cls._format_result(res)

    @classmethod
    def copy(cls, rid):
        return cls.execute('copy', rid)

    @classmethod
    def write(cls, rid, vals):
        return cls.execute('write', rid, vals)

    @classmethod
    def create(cls, vals):
        return cls.execute('create', vals)

    @classmethod
    def unlink(cls, rid):
        return cls.execute('unlink', rid)

    @classmethod
    def action_unarchive(cls, ids):
        return cls.execute('action_unarchive', ids)

    @classmethod
    def action_archive(cls, ids):
        return cls.execute('action_archive', ids)

    @classmethod
    def name_get(cls, ids):
        return cls.execute('name_get', ids)

    @classmethod
    def name_search(cls, **kwargs):
        #  {name, args, operator, limit} = kwargs
        return cls.execute('name_search', **kwargs)

    @classmethod
    def onchange(cls, ids, values, field_name, field_onchange):
        res = cls.execute('onchange', ids, values, field_name, field_onchange)
        res['value'] = cls._format_dict(res['value'])
        return res


class EditModel(object):
    def __init__(self, OdooModel, res_id=None, record=None, values=None):
        if not record:
            record = {}
        if not values:
            values = {}

        self.Model = OdooModel
        self.res_id = res_id
        self.record = record
        self.values = values

        self.queue = []

    def commit(self, callback):
        record = self.record
        values = self.values
        res_id = self.res_id
        id2 = self.Model.web_commit(res_id, record, values)
        callback(id2)

    def onchange(self, callback, fname, value):
        ids = self.res_id and [self.res_id] or []
        record = self.record
        values = {}
        values.update(self.values)
        values.update({fname: value})

        result = self.Model.web_onchange(ids, record, values, fname)
        self.values = result['values']

        callback(result)


class Model(BaseModel):
    @classmethod
    def init_edit_model(cls, res_id=None, record=None, values=None):
        return EditModel(cls, res_id, record, values)

    @classmethod
    def _commit_get_readonly(cls, meta, state):
        readonly = meta['readonly']
        if not state:
            return readonly

        states = meta.get('states')
        if not states:
            return readonly

        state2 = meta.states.get(state)

        if not state2:
            return readonly

        state3 = dict(state2)

        if 'readonly' in state3:
            return state3['readonly']

        return readonly

    @classmethod
    def _get_values_for_write(cls, state, values):
        values2 = {}
        for fld, val in values.items():
            meta = cls._fields.get(fld, {})
            readonly = cls._commit_get_readonly(meta, state)
            if not readonly:
                if val and meta.get('type') == 'many2one':
                    values2[fld] = val[0]
                else:
                    values2[fld] = val

        return values2

    @classmethod
    def web_commit(cls, res_id, record=None, values=None):
        if not values:
            return

        state_value = None
        if 'state' in values:
            state_value = values['state']
        else:
            state_value = record.get('state')

        values2 = cls._get_values_for_write(state_value, values)

        if res_id:
            cls.write(res_id, values2)
            return res_id
        else:
            return cls.create(values2)

    @classmethod
    def _get_values_for_onchange(cls, record=None, values=None):

        if not record:
            record = {}
        if not values:
            values = {}

        all_keys = {}
        all_keys.update(record)
        all_keys.update(values)

        all_keys = list(all_keys.keys())

        def fn(fld):
            meta = cls._fields.get(fld, {})
            meta_type = meta.get('type')
            if meta_type == 'many2many':
                if fld in values:
                    return values[fld]
                else:
                    return [(6, False, record[fld] or [])]
            elif meta_type == 'one2many':
                if fld in values:
                    return values[fld]
                else:
                    return [(4, rid, False) for rid in record[fld] or []]

            else:
                val = None
                if fld in values:
                    val = values[fld]
                else:
                    val = record[fld]

                if not val:
                    return val

                if not meta_type == 'many2one':
                    return val
                else:
                    return val[0]

        return dict((fld, fn(fld)) for fld in all_keys)

    @classmethod
    def web_onchange_new(cls, values=None):
        field_onchange = dict((fld, '1') for fld in cls._fields.keys())

        res = cls.onchange([], values, [], field_onchange)
        res_value = {}
        res_value.update(values)
        res_value.update(res['value'])

        res['value'] = res_value

        return res

    @classmethod
    def web_onchange(cls, ids, record=None, values=None, fname=None):

        field_onchange = dict((fld, '1') for fld in cls._fields.keys())
        vals_onchg = cls._get_values_for_onchange(record, values)

        res = cls.onchange(ids, vals_onchg, fname, field_onchange)

        res_value = {}
        res_value.update(values)
        res_value.update(res['value'])

        res['values'] = res_value

        return res
